package controller

import (
	"context"
	"fmt"
	"github.com/streadway/amqp"
	"goods_seckill_srv/data_source"
	"goods_seckill_srv/models"
	seckill "goods_seckill_srv/proto/seckill"
	"goods_seckill_srv/redisConn"
	"goods_seckill_srv/utils"
	"sync"
	"time"
)

type Seckill struct {

}

func (s *Seckill)Seckill(ctx context.Context, in *seckill.SeckillRequest, out *seckill.SeckillResponse) error {
	id := in.Id
	/*
	秒杀逻辑
	1、下单成功后数据库库存减一
	存在问题： 判断商品是否缺货   一个人不能重复下单   考虑高并发的情况下
	(1) 开始时间限制
		当前时间必须大于等于 开始时间
	(2) 结束时间限制
		 当前时间必须小于 结束时间
	(3) 库存数量限制
		 库存必须大于0
	(4) 每个用户只能购买一个， 先生成订单
	 */

	// 数据库查询数据
	secKills := models.SecKills{}
	result := data_source.Db.Where("id = ?", id).Find(&secKills)
	if result.Error != nil {    // 活动不存在
		out.Code = 500
		out.Msg = "下单失败，请重新下单"
		return nil
	}

	// 当前时间
	nowTime := time.Now()

	// 开始时间限制    当前时间必须大于等于 开始时间
	startTimeRes := result.Where("start_time <= ?", nowTime).Find(&secKills)
	if startTimeRes.Error != nil {
		out.Code = 500
		out.Msg = "抢购还未开始"
		return nil
	}

	// 结束时间限制    当前时间必须小于 结束时间
	endTimeRes := startTimeRes.Where("end_time > ?", nowTime).Find(&secKills)
	if endTimeRes.Error != nil {
		out.Code = 500
		out.Msg = "你来晚了，抢购已经结束了"
		return nil
	}

	// 库存必须大于0
	numRes := endTimeRes.Where("num > 0").Find(&secKills)
	if numRes.Error != nil {
		out.Code = 500
		out.Msg = "商品已抢购完"
		return nil
	}

	// 判断用户是否下单  订单表
	userEmail := in.UserEmail
	order := models.Orders{}
	orderExist := data_source.Db.Where("user_email = ?", userEmail).Where("sid = ?", id).Find(&order)
	if orderExist.Error == nil {   // 说明该用户已经抢购过了
		out.Code = 500
		out.Msg = "你已经抢购过该商品，不能再次抢购"
		return nil
	}


	// 开始下单  库存减一
	err := result.Update("num", secKills.Num - 1).Error
	if err != nil {
		out.Code = 500
		out.Msg = "下单失败，请重新下单"
		return nil
	}


	// 下单成功  生成订单
	createOrder := models.Orders{
		UserEmail: userEmail,
		Sid:       int(id),
		CreateTime:time.Now(),
	}
	createOrderRes := data_source.Db.Create(&createOrder)
	if createOrderRes.Error != nil {
		out.Code = 500
		out.Msg = "下单失败，请重新下单"
	}
	out.Code = 200
	out.Msg = "下单成功"

	return nil
}


var (
	lock sync.Mutex
)

// 消费者消费消息
func init() {

	conn, _ := amqp.Dial("amqp://guest:guest@127.0.0.1:5672/")
	defer conn.Close()

	// 创建通道
	ch, _ := conn.Channel()
	defer ch.Close()
	// 限流  每次处理多少请求
	ch.Qos(10,0, false)

	// 创建消费者
	deliveries, err := ch.Consume("orderQueue", "orderConsume", false, false, false, false, nil)
	if err != nil {

	}
	for delivery := range deliveries {
		fmt.Println("接收到任务")
		go OrderApply(delivery)
	}
}

func OrderApply(delivery amqp.Delivery) {
	body := delivery.Body   // 取出消息

	data := utils.StringToMap(body)

	// 请求数据
	id := data["id"]
	userEmail := data["email"].(string)

	/*
		秒杀逻辑
		1、下单成功后数据库库存减一
		存在问题： 判断商品是否缺货   一个人不能重复下单   考虑高并发的情况下
		(1) 开始时间限制
			当前时间必须大于等于 开始时间
		(2) 结束时间限制
			 当前时间必须小于 结束时间
		(3) 库存数量限制
			 库存必须大于0
		(4) 每个用户只能购买一个， 先生成订单
	*/

	// 解决重复消费问题
	orderMQ := models.Orders{}
	var countMQ int
	data_source.Db.Where("user_email = ?",userEmail).Find(&orderMQ).Count(&countMQ)

	if countMQ > 0 {
		delivery.Ack(true)
		return
	}


	// 数据库查询数据
	secKills := models.SecKills{}
	lock.Lock()
	result := data_source.Db.Where("id = ?", id).Find(&secKills)
	if result.Error != nil {    // 活动不存在
		delivery.Ack(true)
		response := map[string]interface{}{
			"code": 500,
			"msg": "下单失败，请重新下单",
		}
		// 先查询redis中key是否存在
		get := redisConn.RedisDb.Get(userEmail).Err()
		if get == nil {
			return
		}


		// 把确认消息存入redis
		redisConn.RedisDb.Set(userEmail, utils.MapToString(response), 0)
		return
	}

	// 当前时间
	nowTime := time.Now()

	// 开始时间限制    当前时间必须大于等于 开始时间
	startTimeRes := result.Where("start_time <= ?", nowTime).Find(&secKills)
	if startTimeRes.Error != nil {
		//out.Code = 500
		//out.Msg = "抢购还未开始"
		//return nil
		delivery.Ack(true)
		response := map[string]interface{}{
			"code": 500,
			"msg": "抢购还未开始",
		}
		// 先查询redis中key是否存在
		get := redisConn.RedisDb.Get(userEmail).Err()
		if get == nil {
			return
		}


		// 把确认消息存入redis
		redisConn.RedisDb.Set(userEmail, utils.MapToString(response), 0)
		return
	}

	// 结束时间限制    当前时间必须小于 结束时间
	endTimeRes := startTimeRes.Where("end_time > ?", nowTime).Find(&secKills)
	if endTimeRes.Error != nil {
		//out.Code = 500
		//out.Msg = "你来晚了，抢购已经结束了"
		//return nil
		delivery.Ack(true)
		response := map[string]interface{}{
			"code": 500,
			"msg": "你来晚了，抢购已经结束了",
		}
		// 先查询redis中key是否存在
		get := redisConn.RedisDb.Get(userEmail).Err()
		if get == nil {
			return
		}


		// 把确认消息存入redis
		redisConn.RedisDb.Set(userEmail, utils.MapToString(response), 0)
		return
	}

	// 库存必须大于0
	numRes := endTimeRes.Where("num > 0").Find(&secKills)
	if numRes.Error != nil {
		//out.Code = 500
		//out.Msg = "商品已抢购完"
		//return nil
		delivery.Ack(true)
		response := map[string]interface{}{
			"code": 500,
			"msg": "商品已抢购完",
		}
		// 先查询redis中key是否存在
		// 先查询redis中key是否存在
		get := redisConn.RedisDb.Get(userEmail).Err()
		if get == nil {
			return
		}


		// 把确认消息存入redis
		redisConn.RedisDb.Set(userEmail, utils.MapToString(response), 0)
		return
	}

	// 判断用户是否下单  订单表
	order := models.Orders{}
	orderExist := data_source.Db.Where("user_email = ?", userEmail).Where("sid = ?", id).Find(&order)
	if orderExist.Error == nil {   // 说明该用户已经抢购过了
		//out.Code = 500
		//out.Msg = "你已经抢购过该商品，不能再次抢购"
		//return nil
		delivery.Ack(true)
		response := map[string]interface{}{
			"code": 500,
			"msg": "你已经抢购过该商品，不能再次抢购",
		}
		// 先查询redis中key是否存在
		// 先查询redis中key是否存在
		get := redisConn.RedisDb.Get(userEmail).Err()
		if get == nil {
			return
		}


		// 把确认消息存入redis
		redisConn.RedisDb.Set(userEmail, utils.MapToString(response), 0)
		return
	}


	// 开始下单  库存减一
	err := result.Update("num", secKills.Num - 1).Error
	if err != nil {
		//out.Code = 500
		//out.Msg = "下单失败，请重新下单"
		//return nil
		delivery.Ack(true)
		response := map[string]interface{}{
			"code": 500,
			"msg": "下单失败，请重新下单",
		}
		// 先查询redis中key是否存在
		// 先查询redis中key是否存在
		get := redisConn.RedisDb.Get(userEmail).Err()
		if get == nil {
			return
		}


		// 把确认消息存入redis
		redisConn.RedisDb.Set(userEmail, utils.MapToString(response), 0)
		return
	}
	lock.Unlock()


	// 下单成功  生成订单
	createOrder := models.Orders{
		UserEmail: userEmail,
		Sid:       id.(int),
		CreateTime:time.Now(),
	}
	createOrderRes := data_source.Db.Create(&createOrder)
	if createOrderRes.Error != nil {
		//out.Code = 500
		//out.Msg = "下单失败，请重新下单"
		delivery.Ack(true)
		response := map[string]interface{}{
			"code": 500,
			"msg": "下单失败，请重新下单",
		}
		// 先查询redis中key是否存在
		// 先查询redis中key是否存在
		get := redisConn.RedisDb.Get(userEmail).Err()
		if get == nil {
			return
		}


		// 把确认消息存入redis
		redisConn.RedisDb.Set(userEmail, utils.MapToString(response), 0)
		return
	}
	//out.Code = 200
	//out.Msg = "下单成功"
	//
	//return nil

	delivery.Ack(true)
	response := map[string]interface{}{
		"code": 200,
		"msg": "下单成功",
	}
	// 先查询redis中key是否存在
	get := redisConn.RedisDb.Get(userEmail).Err()
	if get == nil {
		return
	}


	// 把确认消息存入redis
	redisConn.RedisDb.Set(userEmail, utils.MapToString(response), 0)
	return

}
